package com.company.emallbackend.service.impl;

import cn.hutool.core.thread.ThreadUtil;
import cn.hutool.core.util.RandomUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.crypto.digest.BCrypt;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.company.emallbackend.constant.RedisConst;
import com.company.emallbackend.entity.User;
import com.company.emallbackend.entity.dto.LoginDTO;
import com.company.emallbackend.entity.dto.RegisterDTO;
import com.company.emallbackend.entity.dto.ResetPwdDTO;
import com.company.emallbackend.entity.vo.LoginVo;
import com.company.emallbackend.exception.CommonException;
import com.company.emallbackend.mapper.UserMapper;
import com.company.emallbackend.service.IUserService;
import com.company.emallbackend.utils.EmailUtils;
import com.company.emallbackend.utils.RedisUtils;
import com.company.emallbackend.utils.TokenUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import javax.annotation.Resource;
import java.util.List;
import java.util.Objects;
import java.util.UUID;
import java.util.concurrent.TimeUnit;

/**
 * <p>
 *  服务实现类
 * </p>
 *
 * @author hanlijian
 * @since 2023-01-11
 */
@Service
@Transactional(rollbackFor = Exception.class)
@Slf4j
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements IUserService {
    @Resource
    private UserMapper userMapper;
    
    @Resource
    private EmailUtils emailUtils;
    
    @Override
    public LoginVo login(LoginDTO loginDTO) {
        if(Objects.isNull(loginDTO)){
            throw new CommonException(201,"系统繁忙,稍后再试");
        }
        String name = loginDTO.getName();
        String password = loginDTO.getPassword();
        if(!StringUtils.hasText(name) || !StringUtils.hasText(password)){
            throw new CommonException(201,"请检查您的登录信息");
        }
        QueryWrapper<User> wrapper = new QueryWrapper<User>().eq("name", name).or().eq("email", name);
        User dbUser = userMapper.selectOne(wrapper);
        if(Objects.isNull(dbUser)){
            throw new CommonException(201,"用户名或邮箱错误,请重新登录");
        }
        if(!BCrypt.checkpw(password,dbUser.getPassword())){
            throw new CommonException(201,"请检查您的密码");
        }
        String token = TokenUtils.genToken(dbUser.getId().toString());
        return new LoginVo(token,dbUser);
    }

    @Override
    public void register(RegisterDTO registerDTO) {
        if(Objects.isNull(registerDTO)){
            throw new CommonException(201,"请检查您的注册信息");
        }
        String name = registerDTO.getName();
        String password = registerDTO.getPassword();
        String email = registerDTO.getEmail();
        String code = registerDTO.getCode();
        if(!StringUtils.hasText(name) || !StringUtils.hasText(password) || 
        !StringUtils.hasText(email) || !StringUtils.hasText(code)){
            throw new CommonException(201,"系统繁忙,稍后再试");
        }
        QueryWrapper<User> wrapper = new QueryWrapper<User>().eq("name", name).or().eq("email", email);
        List<User> users = userMapper.selectList(wrapper);
        if(!CollectionUtils.isEmpty(users)){
            throw new CommonException(201,"用户名或邮箱也被注册");
        }
        //校验邮箱验证码
        boolean flag=checkCode(code,email,"register");
        if(flag){
            User user=new User();
            //添加用户
            String nickname = registerDTO.getNickname();
            if(StrUtil.isNotBlank(nickname)){
                user.setNickname(nickname);
            }else {
                user.setNickname("emall_"+ UUID.randomUUID().toString().replaceAll("-",""));
            }
            user.setName(name);
            user.setPassword(BCrypt.hashpw(password));
            user.setEmail(email);
            this.save(user);
        }
    }

    @Override
    public void sendEmail(String email,String type) {
        if(StringUtils.isEmpty(email)){
            throw new CommonException(201,"系统繁忙,稍后再试");
        }
        if(StringUtils.isEmpty(type)){
            throw new CommonException(201,"系统繁忙,稍后再试");
        }
        if(StrUtil.isBlank(type) ||  (!"register".equals(type) && !"restPassword".equals(type))){
            throw new CommonException(201,"发送邮箱失败");
        }
        //根据邮箱获取RedisCode
        Integer redisCode = RedisUtils.getCacheObject(RedisConst.EMAIL_CODE_PREFIX +type+":"+ email);
        Long expireTime = RedisUtils.getExpireTime(RedisConst.EMAIL_CODE_PREFIX +type+":"+ email);
        if(Objects.nonNull(redisCode) && expireTime>5*60){
            throw new CommonException(201,"邮箱验证过于频繁");
        }
        //生成6位验证码
        String code = RandomUtil.randomNumbers(6);
        log.info("验证码:{}",code);
        //发送验证码
        //注册的时候发送的邮箱验证
        String context = "<b>尊敬的用户：</b><br><br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;您好，" +
                "EMall电商网站提醒您本次的验证码是：<b>{}</b>，" +
                "有效期5分钟。<br><br><br><b>EMall电商网站</b>";
        String html = StrUtil.format(context, code);
        User user = userMapper.selectOne(new QueryWrapper<User>().eq("email", email));
        if("register".equals(type) && Objects.nonNull(user)){
            throw new CommonException(201,"该邮箱已被注册");
        }
        if("restPassword".equals(type) && Objects.isNull(user)){
            throw new CommonException(201,"该用户还未注册");
        }
        //发送邮箱
        ThreadUtil.execAsync(()->{
            emailUtils.sendHtml("【EMall网站】验证提醒",html,email);
            RedisUtils.setCacheObject(RedisConst.EMAIL_CODE_PREFIX +type+":"+ email,code,5L, TimeUnit.MINUTES);
        });
    }

    @Override
    public void passwordRest(ResetPwdDTO resetPwdDTO) {
        if(Objects.isNull(resetPwdDTO)){
            throw new CommonException(201,"系统繁忙,稍后再试");
        }
        String email = resetPwdDTO.getEmail();
        String code = resetPwdDTO.getCode();
        if(StrUtil.isBlank(email) || Objects.isNull(code)){
            throw new CommonException(201,"请检查您的重置密码信息");
        }
        //查询该邮箱是否注册过
        User user = userMapper.selectOne(new QueryWrapper<User>().eq("email", email));
        if(Objects.isNull(user)){
            throw new CommonException(201,"该邮箱并未被注册,请检查后重置");
        }
        //检查验证码
        boolean flag = checkCode(code, email, "restPassword");
        if(!flag){
            throw new CommonException(201,"验证码有误");
        }
        //将数据库密码修改为123
        user.setPassword(BCrypt.hashpw("123"));
        userMapper.updateById(user);
    }

    private boolean checkCode(String code, String email,String type) {
        String redisCode = RedisUtils.getCacheObject(RedisConst.EMAIL_CODE_PREFIX + type+":"+email);
        if(Objects.isNull(redisCode)){
            throw new CommonException(201,"验证已失效");
        }
        if(!redisCode.equals(code)){
            throw new CommonException(201,"验证码错误");
        }
        RedisUtils.deleteObject(RedisConst.EMAIL_CODE_PREFIX+type+":"+email);
        return true;
    }

}
